package net.hwii.obj;

import java.util.Date;

import net.hwii.core.BlockChain;

/**
 *  router.bittorrent.com:6881
 router.utorrent.com:6881
 * @author Administrator
 *
 */
public class Node {
	private String nodeId;//16进制字符串

    private String ip; //node的ip

    private Integer port; //node的端口

    private Date updateTime;//最后更新时间

    private byte[] nodeIdBytes;//20字节

    private Integer k=0;//k桶应该有的位置

    private Integer currentK=0;//当前的位置

    private Integer rank=0; //node的rank分值 ，路由表满的时候，优先移除分值低的
    private Map<Integer,PriorityQueue<Node>> tableMap=new ConcurrentHashMap<>();
	public String sendMessage(String msg) {
		return null;
	}
	
	public int createChain(BlockChain chain) {
		return 0;
	}
	
	public void put(Node node) {
	    int bucketIndex = getBucketIndex(node);
	    if(bucketIndex==0){//是自己就不用加入了
	        return;
	    }
	    PriorityQueue<Node> pq = tableMap.get(bucketIndex);
	    if(CollectionUtils.isEmpty(pq)){
	        //如果是空 那么找最近的那个节点加入
	        boolean isAdd=false;
	        while(bucket.next != null){
	            if(bucketIndex > bucket.getK()
	                    && bucketIndex < bucket.next.getK()){
	                 //先往小的里面放
	                node.setCurrentK(bucket.getK());
	                isAdd=putAccurate(tableMap.get(bucket.getK()),node,false,bucket,tableMap);
	                if(!isAdd){
	                    node.setCurrentK(bucket.next.getK());
	                    isAdd=putAccurate(tableMap.get(bucket.next.getK()),node,true,bucket,tableMap);
	                }
	            }
	            bucket=bucket.next;

	        }
	        if(!isAdd){
	            //没有添加成功 那么往最后一个节点添加
	            node.setCurrentK(bucket.getK());
	            putAccurate(tableMap.get(bucket.getK()),node,true,bucket,tableMap);
	        }

	    }else{//如果不空 那么直接加 简单点来吧
	        if(pq.size()<8){
	            if(!pq.contains(node)){
	                node.setCurrentK(node.getK());
	                pq.add(node);
	            }else{
	                reAdd(pq,node);
	            }
	        }else{
	            pq.add(node);
	            pq.poll();
	        }
	    }
	}
	
	/**
	 * @param pq 当前bucket
	 * @param node 需要插入的node
	 * @param isSplit 是否需要分裂
	 * @param bucket 需要插入的bucket的位置
	 * @param tableMap 路由表
	 * @return 返回是否添加成功
	 */
	@SneakyThrows
	public boolean putAccurate(PriorityQueue<Node> pq,Node node,boolean isSplit,Bucket bucket,Map<Integer,PriorityQueue<Node>> tableMap){
	    boolean isAdd=false;
	    if(pq.contains(node)){
	        return reAdd(pq,node);
	    }
	    if(pq.size()<8){
	        pq.add(node);
	        isAdd=true;
	    }
	    if(isSplit && !isAdd){
	        PriorityQueue<Node> priorityQueue=new PriorityQueue<Node>((x,y)->x.getRank()-y.getRank());
	        priorityQueue.add(node);
	        tableMap.putIfAbsent(node.getK(),priorityQueue);
	        //创建新的k桶后需要把两边的bucket距离比较近的都放到自己的k桶里面 如果超过8个就丢了 最好是可以ping一下
	        //先从小的开始放
	        PriorityQueue<Node> collect1 = new PriorityQueue<>();
	        collect1.addAll(tableMap.get(bucket.getK()).stream().filter(n -> {
	            if (priorityQueue.size() < 8 &&
	                    Math.abs(n.getK() - n.getCurrentK()) > Math.abs(n.getK() - node.getK())) {
	                n.setCurrentK(node.getK());
	                priorityQueue.add(n);
	                return false;
	            }
	            return true;
	        }).collect(Collectors.toSet()));
	        tableMap.put(bucket.getK(),CollectionUtils.isNotEmpty(collect1)?collect1:new PriorityQueue<Node>());
	        if(bucket.next!=null && CollectionUtils.isNotEmpty(tableMap.get(bucket.next.getK()))){
	            PriorityQueue<Node> collect = new PriorityQueue<>();
	            collect.addAll(tableMap.get(bucket.next.getK()).stream().filter(n -> {
	                if (priorityQueue.size() < 8 &&
	                        Math.abs(n.getK() - n.getCurrentK()) > Math.abs(n.getK() - node.getK())) {
	                    n.setCurrentK(node.getK());
	                    priorityQueue.add(n);
	                    return false;
	                }
	                return true;
	            }).collect(Collectors.toSet()));
	            tableMap.put(bucket.next.getK(),CollectionUtils.isNotEmpty(collect)?collect:new PriorityQueue<Node>());
	        }
	        Bucket b=new Bucket(node.getK(),bucket.next);
	        bucket.next=b;
	        isAdd=true;
	        node.setCurrentK(node.getK());
	    }
	    return isAdd;
	}
	/**
	* 根据nodeid 查找最近的8个node
	* @param trargetBytes 需要查找目标id
	 * @return
	 */
	public List<Node> getForTop8(byte[] trargetBytes){
	    int bucketIndex = getBucketIndex(trargetBytes);
	    List<Node> l=new ArrayList<>();
	    PriorityQueue<Node> pq = tableMap.get(bucketIndex);
	    if(CollectionUtils.isEmpty(pq)){
	        while(bucket.next != null){
	            if(bucketIndex > bucket.getK()
	                    && bucketIndex < bucket.next.getK()){

	                tableMap.get(bucket.next.getK()).stream().forEach(x->{
	                    if(l.size()<8){
	                        l.add(x);
	                    }
	                });
	            }
	            bucket=bucket.next;
	        }
	        if(CollectionUtils.isEmpty(l)){
	            tableMap.get(bucket.getK()).stream().forEach(x->{
	                if(l.size()<8){
	                    l.add(x);
	                }
	            });
	        }

	    }else{//如果不空 那么直接加 简单点来吧
	        l.addAll(pq.stream().collect(Collectors.toList()));
	    }
	    return l;
	}
	

    /**
     * find_node响应 权重10
     * 
     * @param packet
     * @param decodeMap
     */
    public void findNode(DatagramPacket packet, Map<String, Object> decodeMap) {
        //解析数据
        @SuppressWarnings("unchecked")
        Map<String, String> responseMap = (Map<String, String>) decodeMap.get(DhtParamEnum.RESPONSE.getCode());
        //将返回数据者加入我们自己的路由表中
        Node node = new Node(packet.getAddress().getHostAddress(), packet.getPort(),
                ByteUtils.string2Bytes(responseMap.get(FindNodeResponse.ID)), LocalDateTime.now(), 10).encode();
        routingTable.add(node);
        //解析其返回的nodes节点
        String nodesStr = responseMap.get(FindNodeResponse.NODES);
        List<Node> nodes = resolveNodes(nodesStr);
        //对返回的nodes继续发送find_node请求
        for (Node tempNode : nodes) {
            messageSenderService.findNode(tempNode);
        }
    }
    /**
     * 对指定节点发送find_node请求
     * 
     * @param node
     */
    public void findNode(Node node) {
        if (!node.check()) {
            //node检查不通过
            return;
        }
        //构建find_node消息体
        FindNodeRequest findNodeRequest = new FindNodeRequest();
        findNodeRequest.setId(localNode.getNodeId());
        findNodeRequest.setTarget(GenerateUtils.generateNodeId());
        RequestMessage<FindNodeRequest> findNodeMessage = new RequestMessage<FindNodeRequest>(findNodeRequest);
        ThreadPool.findNodeSend.execute(new Runnable() {
            @Override
            public void run() {
                //Bencode要发送的数据
                byte[] data = bencode.encode(findNodeMessage);
                //System.out.println(new String(data, Charset.forName("ISO-8859-1")));
                try {
                    Thread.sleep(20);
                    //将消息放入发送列表中
                    sendTable.put(findNodeMessage.getT(), findNodeMessage.getQ());
                    datagramSocket.send(
                            new DatagramPacket(data, data.length, InetAddress.getByName(node.getIp()), node.getPort()));
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
